package com.jvyou.app.ds.utils.random;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;

import java.security.SecureRandom;

/**
 * 邀请码生成工具类
 * <p>
 * 使用Snowflake算法生成唯一ID，并通过Base32编码转换为短字符串形式的邀请码
 * 特点：
 * 1. 全大写字母和数字，排除易混淆字符
 * 2. 可选校验位防止输入错误
 * 3. 线程安全
 * </p>
 *
 * @author 橘柚
 * @since 2025/3/24 09:27
 */
public final class InviteCodeUtil {
    // 使用安全的随机数生成器
    private static final SecureRandom SECURE_RANDOM = new SecureRandom();

    // 优化后的Base32字符集 (排除0/O, 1/I, 2/Z等易混淆字符)
    private static final String BASE32 = "3456789ABCDEFGHJKLMNPQRSTUVWXY";
    private static final int BASE32_LENGTH = BASE32.length();

    // 默认邀请码长度 (包含1位校验位)
    private static final int DEFAULT_CODE_LENGTH = 8;

    // 是否启用校验位
    private static boolean enableCheckDigit = true;

    // Snowflake实例 (使用双重检查锁模式)
    private static volatile Snowflake snowflake;

    private InviteCodeUtil() {
        throw new UnsupportedOperationException("Utility class");
    }

    /**
     * 初始化Snowflake生成器
     *
     * @param workerId     工作机器ID (0~31)
     * @param datacenterId 数据中心ID (0~31)
     */
    public static synchronized void initSnowflake(long workerId, long datacenterId) {
        if (workerId < 0 || workerId > 31) {
            throw new IllegalArgumentException("Worker ID must be between 0 and 31");
        }
        if (datacenterId < 0 || datacenterId > 31) {
            throw new IllegalArgumentException("Datacenter ID must be between 0 and 31");
        }
        snowflake = IdUtil.getSnowflake(workerId, datacenterId);
    }

    /**
     * 设置是否启用校验位
     *
     * @param enable 是否启用校验位
     */
    public static void setEnableCheckDigit(boolean enable) {
        enableCheckDigit = enable;
    }

    private static Snowflake getSnowflake() {
        if (snowflake == null) {
            synchronized (InviteCodeUtil.class) {
                if (snowflake == null) {
                    snowflake = IdUtil.getSnowflake(1, 1);
                }
            }
        }
        return snowflake;
    }

    /**
     * 生成唯一ID (使用Snowflake算法)
     */
    public static long generateId() {
        return getSnowflake().nextId();
    }

    /**
     * 生成邀请码 (Base32编码)
     */
    public static String generateInviteCode() {
        return generateInviteCode(DEFAULT_CODE_LENGTH);
    }

    /**
     * 生成指定长度的邀请码
     *
     * @param length 邀请码长度 (必须大于0)
     * @return 邀请码字符串
     */
    public static String generateInviteCode(int length) {
        if (length <= 0) {
            throw new IllegalArgumentException("Length must be positive");
        }

        long id = generateId();
        String code = toBase32(id, enableCheckDigit ? length - 1 : length);

        if (enableCheckDigit) {
            code += calculateCheckDigit(code);
        }

        return code;
    }

    /**
     * 将数字转换为Base32字符串
     */
    private static String toBase32(long num, int minLength) {
        if (num <= 0) {
            throw new IllegalArgumentException("Number must be positive");
        }

        StringBuilder sb = new StringBuilder();
        long remaining = num;

        do {
            sb.append(BASE32.charAt((int) (remaining % BASE32_LENGTH)));
            remaining /= BASE32_LENGTH;
        } while (remaining > 0);

        String code = sb.reverse().toString();

        // 长度不足时填充随机字符
        while (code.length() < minLength) {
            code = BASE32.charAt(SECURE_RANDOM.nextInt(BASE32_LENGTH)) + code;
        }

        return code;
    }

    /**
     * 计算校验位 (简单加权求和模运算)
     */
    private static char calculateCheckDigit(String code) {
        int sum = 0;
        for (int i = 0; i < code.length(); i++) {
            sum += (i % 2 == 0) ? code.charAt(i) * 2 : code.charAt(i);
        }
        return BASE32.charAt(sum % BASE32_LENGTH);
    }

    /**
     * 验证邀请码有效性 (校验位检查)
     */
    public static boolean validateInviteCode(String code) {
        if (!enableCheckDigit || code == null || code.isEmpty()) {
            return true;
        }

        String mainPart = code.substring(0, code.length() - 1);
        char expectedCheckDigit = calculateCheckDigit(mainPart);
        return code.charAt(code.length() - 1) == expectedCheckDigit;
    }

    public static void main(String[] args) {
        // 生成示例
        System.out.println("Generating 10 invite codes:");
        for (int i = 0; i < 10; i++) {
            String code = generateInviteCode();
            System.out.printf("Code %2d: %s (Valid: %b)%n",
                    i + 1, code, validateInviteCode(code));
        }

        // 测试校验位
        String testCode = "ABCDEFGH";
        String withCheckDigit = testCode + calculateCheckDigit(testCode);
        System.out.printf("Test validation: %s => %b%n",
                withCheckDigit, validateInviteCode(withCheckDigit));
    }
}